using System;
using System.Data;

namespace ODX.Core
{
    /// <summary>
    /// This interface should be implemented for the custom hierarchy type resolver.
    /// </summary>
    public interface IEntityFactory
    {
        /// <summary>
        /// Defines <see cref="Entity"/> type for the <see cref="DataRow"/>.
        /// </summary>
        /// <param name="row"><see cref="DataRow"/></param>
        /// <returns><see cref="Entity"/> type to use in entity wrapper.</returns>
        Type DefineType(DataRow row);
    }

    internal class TypeDefFactory : IEntityFactory
    {
        private string typeDef;
        public TypeDefFactory(string typeDef)
        {
            this.typeDef = typeDef;
        }

        public Type DefineType(DataRow row)
        {
            Session s = Session.GetSession(row);

            DataRow typeDefRow = FindTypeDefRow(row);
            if (typeDefRow[Session.TypeDefField].ToString() == typeDef)
            {
                Type[] types = s.Pm.GetTypesForTypeDef(typeDef);
                Type typeDefType = s.Pm.GetRootTypeForTable(row.Table.TableName);
                foreach (Type type in types)
                    if (type == typeDefType || type.IsSubclassOf(typeDefType))
                        return type;
            }
            return null;
        }

        private static DataRow FindTypeDefRow(DataRow row)
        {
            Session s = Session.GetSession(row);
            while (!row.Table.Columns.Contains(Session.TypeDefField))
            {
                bool found = false;
                foreach (DataRelation dr in row.Table.DataSet.Relations)
                {
                    if (string.Compare(dr.ChildColumns[0].Table.TableName, row.Table.TableName, true) == 0 &&
                        string.Compare(dr.ChildColumns[0].ColumnName, "ID", true) == 0)
                    {
                        row = s.FindRecord(dr.ParentColumns[0].Table.TableName, row["ID"].ToString());
                        found = true;
                        break;
                    }
                }

                if ( !found ) break;
            }

            if (row.Table.Columns.Contains(Session.TypeDefField))
                return row;

            return null;
        }
    }
}
